#ifndef _STDIO_HEADER_
#define _STDIO_HEADER_
#include <stdio.h>
#endif
//
#ifndef _STRING_HEADER_
#define _STRING_HEADER_
#include <string.h>
#endif
//
#ifndef _STDLIB_HEADER_
#define _STDLIB_HEADER_
#include <stdlib.h>
#endif
//
#ifndef _CTYPE_HEADER_
#define _CTYPE_HEADER_
#include <ctype.h>
#endif
//
#include "getputch.h"
//
#include "data_interface.h" //不必加上ifndef，因为在建立该头文件时，已经应用了ifndef。
//
#include "basicfun_interface.h" //加入此项，不必再顾忌各级函数的顺序位置。
//
// 01 一级函数（最基本子函数）
// 1.1 帮助信息输出
void help_print()
{
    if (if_help == YES)
    {
        printf("\n>>> Usage: ./typex filename.txt <-opt>\n");
        printf("\t<-g> <-goal>: the goal you set.\n");
        printf("\t<-rand>: if use rand sequence of abc.\n");
        printf("\t<-h>: print this in stdout.\n");
        printf("\t<-ctrl> <ctrl_file>: if you want practice like words in file, add this.\n");
        exit(0);
    }
    return;
}
/* -------------------------------------------------------------------------- */
// 1.1.1 程序抬头输出
void info_print()
{
    printf("\n\
===============================================\n\
                                               \n\
  ######  ##    ##  #####   #####  ##    ##  \n\
    ##     ##  ##   ##  ##  ##      ##  ##   \n\
    ##      ####    #####   #####    ####    \n\
    ##       ##     ##      ##      ##  ##   \n\
    ##       ##     ##      #####  ##    ##  \n\
                 -v3.0 bata-                   \n\
===============================================\n\n");
    // switch (type_mode)
    // {
    // case 100:
    //     printf("\n>>> 当前是常规模式，您还可以选择：随机模式（-rand，随机打印abc...）和ctrl文本模式（-ctrl <file_name> ）\n");
    //     break;
    // case 101:
    //     printf("\n>>> 当前是随机模式，您还可以选择：常规模式（默认，顺序打印abc...）和ctrl文本模式（-ctrl <file_name> ）\n");
    //     break;
    // case 102:
    //     printf("\n>>> 当前是ctrl文本模式，您还可以选择：常规模式（默认，顺序打印abc...）和随机模式（-rand，随机打印abc...）\n");
    //     break;
    // default:
    //     break;
    // }
    return;
}
/* -------------------------------------------------------------------------- */
// 1.2 字符串置空函数
void str_clean(char *str, int len)
{ //字符串置空
    int i;
    //
    for (i = 0; i < len; i++)
    {
        str[i] = 0;
    }
    //
    return;
}
/* -------------------------------------------------------------------------- */
//
// 1.3 控制流参数输入判断
int argv_find(const char *str, char *str_group[], int num)
{
    int i;
    int judge;
    //
    judge = -1;
    //
    for (i = 1; i < num; i++)
        if (strcmp(str, str_group[i]) == 0)
        {
            judge = i;
            break;
        }
    //
    return judge;
}
/* -------------------------------------------------------------------------- */
// 1.4 文件初始化
void file_ini()
{
    if ((fp_ctrl = fopen(txt_name, "r")) == NULL)
        printf("**error** Open txt file failed.\n"), exit(0);
    // 生成work_name
    strncpy(work_name, txt_name, strlen(txt_name) - strlen(Judge_dot_txt));
    //
    str_clean(log_name, MAXNAME);
    str_clean(dat_name, MAXNAME);
    strcpy(log_name, work_name);
    strcpy(dat_name, work_name);
    if ((fp_log = fopen(strcat(log_name, ".log"), "a+")) == NULL)
        printf("**error** Open log file failed.\n"), exit(0);
    if ((fp_dat = fopen(strcat(dat_name, ".dat"), "ab+")) == NULL)
        printf("**error** Open dat file failed.\n"), exit(0);

    return;
}
/* -------------------------------------------------------------------------- */
// 1.5 历史情况可视化输出
/* 二进制文件便于操作 */
void graphshow(int right_num, int goal, int mode)
{
    float len;
    int len_int;
    int i, j;
    long loc_dat;
    //
    int num; //dat文件记录的总次数
    //
    int show_num = 20;
    float stretch_ratio;
    int goal0;
    //
    goal0 = goal;
    //
    if (goal <= 0)
        goal = 20; //达到2.0个每秒显示提示符。
    //
    if (show_num < right_num)
        show_num = right_num;
    //
    printf("\n>>> Graph: [number of letter] in average in the last %d exercise.\n", show_num);
    // 定位至近20次记录的开端
    fseek(fp_dat, 0, SEEK_END);
    loc_dat = ftell(fp_dat);
    if (loc_dat % sizeof(float) != 0)
        printf("Error: loc_dat is wrong.\n"), exit(0);
    num = loc_dat / sizeof(float);
    if (num >= show_num)
        num = show_num;
    fseek(fp_dat, -num * sizeof(float), SEEK_END);
    // 读取数字
    for (i = 0; i < num; i++)
    {
        fread((char *)&len, sizeof(float), 1, fp_dat);
        //
        printf("\n%5.2f", len);
        //
        len_int = (int)(len * 10) + 1; //为画图准备，例如len=7.123456， 则len_int=72；若len=7.654321，则len_int=77.
        //
        if (i > num - 1 - right_num) //本次练习标注为*
            printf("* ");
        else
            printf("  ");
        //
        for (j = 0; j < len_int; j++)
        {
            if (j > 129)
            {
                printf("······");
                break;
            }
            if (j != goal)
                printf("———");
            else
                printf("|");
        }
        // 小于goal的成绩，仍要输出goal的标记|
        for (j = len_int; j <= goal; j++)
        {
            if (j != goal)
                printf("   ");
            else
                printf("|");
        }
    }
    printf("\n%7s", "goal: ");
    for (i = 0; i < goal; i++)
    {
        printf("   ");
    }
    printf("%2.1f\n", (float)goal0 / 10.0);
    fclose(fp_dat);
    //
    return;
}
/* -------------------------------------------------------------------------- */
// 1.6 ctrl模式下计算文本行数（即相当于普通模式下的练习次数）
void get_num_lines()
{
    int c;
    //
    num_lines = 1;
    //
    while ((c = getc(fp_ctrl)) != EOF)
        if (c == '\n')
            num_lines++;
    fseek(fp_ctrl, 0, SEEK_SET);
    //
    return;
}
/* -------------------------------------------------------------------------- */
// 1.10大写转小写函数
void caps_unlock(char *str)
{
    int i;
    int len;
    //
    len = strlen(str);
    for (i = 0; i < len; i++)
        if (str[i] >= 65 && str[i] <= 90)
            str[i] = str[i] + 32;
    //
    return;
}
/* -------------------------------------------------------------------------- */
void input_in_same_line()
{
    int i;
    int len_a = 0;
    //
    len_a = strlen(examword);
    //
    for (i = 0; i < len_a; i++)
    {
        printf("%s \r", &examword[i]);
        fflush(stdout);
        //
        while (getch() != examword[i])
            num_letter_wrong++;
    }

    //
    printf("\n");
    //
    return;
}
/* -------------------------------------------------------------------------- */
// 2.二级函数
// 控制流参数输入判断汇总
void running_paras(int argc, char **argv)
{
    int i;
    //
    // 1.确定练习文件名字
    get_filename(argc, argv);
    //
    // 2.确定本次练习模式mode_type
    i = argv_find(Judge_elimination, argv, argc);
    if (i > 0)
        type_mode = mode_elimination;
    else
        type_mode = mode_normal;
    //
    // 3. 是否使用easy模式
    i = argv_find(Judge_easy, argv, argc);
    if (i > 0)
    {
        if_easy = YES;
    }
    else
        if_easy = NO;
    //
    // 4.确定本次练习目标时间
    i = argv_find(Judge_goal, argv, argc);
    if (i > 0)
        sscanf(argv[i + 1], "%f", &t_goal);
    else
        t_goal = 3.0;
    //
    // 5. 是否输出帮助信息
    i = argv_find(Judge_help, argv, argc);
    if (i > 0)
        if_help = YES;
    else
        if_help = NO;
    //
    return;
}
/* -------------------------------------------------------------------------- */
// 2.2 初始化程序
void ini(int argc, char **argv)
{
    // 01 控制流参数判断汇总
    running_paras(argc, argv);
    // 02 文件初始化
    file_ini();
    // 03 输出信息
    info_print();
    help_print();
    // 04 练习次数（即文本行数）
    get_num_lines();
    // 05 分配内存
    examword = (char *)calloc(1000, sizeof(char));
    inword = (char *)calloc(1000, sizeof(char));
    // 06 输出目标
    // printf("\n>>> Goal is %3.1fs\n", t_goal);
    // 07 输入开始
    printf(">>> 请按下换行键开始输入:");
    while (getchar() != '\n')
        ;
    // 07 计时开始
    time_0 = time(NULL);
    //
    return;
}
/* -------------------------------------------------------------------------- */
// 2.3 终止程序
void ender()
{
    float val_all = 0.0;
    float accuracy = 0.0;
    float speed = 0.0;
    // 关闭文件
    fclose(fp_log);
    fclose(fp_dat);
    // 结束
    accuracy = (float)(num_letter - num_letter_wrong) / (float)num_letter;
    printf("\n>>> Letters: %-4d \twrong letters: %-4d \taccuracy: %-.2f%%\n", num_letter, num_letter_wrong, accuracy * 100);
    //
    val_all = (float)(time(NULL) - time_0);
    speed = (float)num_word / (float)val_all;
    printf(">>> Time: %-4.2fs \twords: %-4d \t\tspeed: %-.2f/min\n", val_all, num_word, speed * 60);
    //
    printf(">>> END.\n\n");
    //
    return;
}
/* -------------------------------------------------------------------------- */
// 2.4 生成练习模板examword
void get_examword()
{
    int j;
    int len_a = 0;
    //
    str_clean(examword, 1000);
    if (getc(fp_ctrl) != EOF)
        fseek(fp_ctrl, -1, SEEK_CUR), fgets(examword, 1000, fp_ctrl);
    //
    // 字符串结尾必须以换行结束：为了单词计数，也为了在normal模式中实现enter键结束输入。
    len_a = strlen(examword);
    if (examword[len_a - 1] != '\n')
        strcat(examword, "\n");
    len_a = strlen(examword);
    //
    // 字母计数
    num_letter = num_letter + len_a;
    // 单词计数
    for (j = 0; j < len_a; j++)
        if (isspace(examword[j]))
            num_word++;
    //
    // esay模式下将大写字母全部转换为小写字母
    if (if_easy == YES)
        caps_unlock(examword);
    //
    if (type_mode == mode_elimination)
    {
        // beta版本中，因为使用了getch()函数，所以去掉换行符号
        if (examword[len_a - 1] == '\n')
            examword[len_a - 1] = '\0';
    }
    //
    return;
}
/* -------------------------------------------------------------------------- */
// 2.5 生成本次练习输入inword
void get_inword()
{
    if (type_mode == mode_elimination)
        get_inword_elim();
    else
        get_inword_normal();
    //
    return;
}
/* -------------------------------------------------------------------------- */
// 2.6 判断正确性(inword是否与examword相同）
void correct_work()
{
    if (type_mode == mode_elimination)
        correct_work_elim();
    else
        correct_work_normal();
    //
    return;
}
/* -------------------------------------------------------------------------- */
// v4.0 plus
// 1. 获取输入文件名字
void get_filename(int argc, char **argv)
{
    int i, j;
    char *str;
    //
    int if_get_txt = NO; //是否得到txt文件名
    //
    for (i = 1; i < argc; i++)
    {
        str = strrchr(argv[i], dot);
        if (str != NULL)
        {
            if (strcmp(str, Judge_dot_txt) == 0)
            {
                if_get_txt = YES;
                strcpy(txt_name, argv[i]);
                break;
            }
        }
    }
    //
    if (if_get_txt)
        printf("The input file is: %s\n", txt_name);
    else
        printf("**error** Usage: ./typex filename.txt\n"), exit(0);
    //
    return;
}
/* -------------------------------------------------------------------------- */
// 2 生成本次练习输入inword
void get_inword_elim()
{
    //01 每次输入的计时
    time_start = time(NULL);
    gettimeofday(&t_start, NULL);
    //02 beta版本 getch()函数初始化
    init_getputch();
    //
    printf("\n>>> No.%d of %d, please input:\n", type_ID + 1, num_lines);
    //
    //03 核心：输入
    input_in_same_line();
    //
    //04 getch()函数终止
    term_getputch();
    //
    //05 截止计时
    gettimeofday(&t_end, NULL);
    //
    return;
}
/* -------------------------------------------------------------------------- */
// 3 生成本次练习输入inword
void get_inword_normal()
{
    printf("\n>>> No.%d of %d, please input:\n", type_ID + 1, num_lines);
    //
    // esay模式下将大写字母全部转换为小写字母
    if (if_easy == YES && if_ctrl == YES)
        caps_unlock(examword);
    //
    printf("\n%s\n", examword);
    //2.2 每次输入的计时
    time_start = time(NULL);
    gettimeofday(&t_start, NULL);
    //2.3 输入
    // scanf("%s", inword);
    fgets(inword, 1000, stdin); //除了输入的字符，还会多读进去/n和/0两个字符
    gettimeofday(&t_end, NULL);
    //
    return;
}
/* -------------------------------------------------------------------------- */
// 4. 判断正确性(inword是否与examword相同）
void correct_work_elim()
{
    int j;
    //
    // 1.1 本次计时
    t_val_long = 1000000 * (t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec - t_start.tv_usec;
    t_val = (float)t_val_long / 1000000.0;
    // 成绩统计方式由时间改为每秒键入多少个字母
    t_val = (float)strlen(examword) / t_val;
    // 1.2 输出计时到文本文件
    fprintf(fp_log, "\n%f", t_val);
    // 1.3 输出计时到二进制文件（便于后处理）
    fwrite((char *)&t_val, sizeof(float), 1, fp_dat);
    tright++;

    //
    return;
}
/* -------------------------------------------------------------------------- */
// 5. 判断正确性(inword是否与examword相同）
void correct_work_normal()
{
    int j;
    int len_a = 0;
    int len_b = 0;
    int lenmax = 0; //试题字符串和输入字符串之间，较长字符串的长度。目的：在输入错误的情况下，指出错误所在位置。
    //
    len_a = strlen(examword);
    len_b = strlen(inword);
    //
    lenmax = len_a;
    //
    if (len_b > lenmax)
        lenmax = len_b;
    //
    if (strcmp(examword, inword) == 0)
    {
        // 1 正确情况下：计时
        // 1.1 本次计时
        // val = (float)difftime(time(NULL), time_start); //精度不够，暂时不用了。19-10-10
        t_val_long = 1000000 * (t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec - t_start.tv_usec;
        t_val = (float)t_val_long / 1000000.0;
        // 成绩统计方式由时间改为每秒键入多少个字母
        t_val = (float)strlen(examword) / t_val;
        // 1.2 输出计时到文本文件
        fprintf(fp_log, "\n%f", t_val);
        // 1.3 输出计时到二进制文件（便于后处理）
        fwrite((char *)&t_val, sizeof(float), 1, fp_dat);
        // 1.4 输出计时到主屏幕
        printf("\n>>> Good: <%.2f/s>\n", t_val);
        tright++;
    }
    else
    {
        // 2 错误情况下：指出错误点
        for (j = 0; j < lenmax; j++)
            if (inword[j] == examword[j])
                printf(" ");
            else
                num_letter_wrong++, printf("*");
        printf("\nSad.\n"), twrong++;
    }
    //
    return;
}